/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package locusts.server;

import java.util.EventListener;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import locusts.common.GameConfig;
import locusts.common.Player;
import locusts.common.WorldModel;
import locusts.lib.FrameCounter;

/**
 * Class the controlls the running of a game on the server.
 *
 * @author hamish
 */
public class Game {

    private WorldModel world;
    private GameConfig config;
    private long lastTagTime = 0;
    private Timer timer = null;
    private int modCount;
    private final List<Listener> listeners =
            new CopyOnWriteArrayList<Listener>();
    private FrameCounter frameCounter;

    public Game(WorldModel world, GameConfig config) {
        this.world = world;
        this.config = config;
        modCount = 0;
    }

    public void start() {
        frameCounter = new FrameCounter("Server:Game");
        timer = new Timer("Server Game Thread");
        timer.schedule(new UpdateTimer(), 0, 1000L / config.getUpdateRate());
    }

    public void stop() {
        timer.cancel();
        timer.purge();
    }

    private class UpdateTimer extends TimerTask {

        @Override
        public void run() {
            frameCounter.markFrame();
            checkWhosIt();
            if (modCount > 0) {
                fireGameUpdatedEvent();
                modCount = 0;
            }
            Thread.yield();
        }
    }

    public void addPlayer(Player player) {
        world.addPlayer(player);
        if (world.numPlayers() == 1) {
            world.setIt(player);
        }
        modCount++;
    }

    public void removePlayer(Player player) {
        world.removePlayer(player);
        if (world.isIt(player)) {
            if (world.numPlayers() > 0)
                world.setIt(world.getAnyPlayer());
            else world.clearIt();
        }
        modCount++;
    }

    public boolean playerExists(String name) {
        return world.playerExists(name);
    }

    public WorldModel getWorld() {
        return world;
    }

    private void checkWhosIt() {
        if (world.numPlayers() == 0)
            return;

        if (System.currentTimeMillis() - lastTagTime < config.
                getTagTimer())
            return;

        Player it = world.getIt();
        for (Player p : world.getPlayers()) {
            if (p.equals(it))
                continue;
            if (p.distanceTo(it) <= config.getTagRadius() * 2) {
                world.setIt(p);
                lastTagTime = System.currentTimeMillis();
                modCount++;
                break;
            }
        }
    }

    void updatePlayer(Player p) {
        world.updatePlayer(p);
        modCount++;
    }

    public GameConfig getConfig() {
        return config;
    }

    private void fireGameUpdatedEvent() {
        for (Listener listener : listeners) {
            listener.gameUpdate(this);
        }
    }

    public void addGameListener(Listener listener) {
        if (listener == null)
            throw new NullPointerException();
        if (!listeners.contains(listener))
            listeners.add(listener);
    }

    public void removeGameListener(Listener listener) {
        if (listener == null)
            throw new NullPointerException();
        listeners.remove(listener);
    }

    public interface Listener extends EventListener {

        void gameUpdate(Game game);
    }
}
